//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __ELASTOS_CONTEXT_H__
#define __ELASTOS_CONTEXT_H__

#ifdef _neptune
#define THREAD_STACK_ORDER         2
#else
#define THREAD_STACK_ORDER         1
#endif

#include <asm.h>

#if defined(_GNUC)

INLINE Thread *GetCurrentThread()
{
    Thread *pThread;

    ASM("mov    %0, sp, lsr %1;"
        "mov    %0, %0, lsl %1;"
        :"=r"(pThread) :"i"(PAGE_SHIFT + THREAD_STACK_ORDER));

    return pThread;
}
#elif defined(_EVC)

EXTERN_C Thread *GetCurrentThread();

#else

#error Unknown C++ compiler

#endif // defined(_GNUC)

//
// ThreadHContext
//
class ThreadHContext
{
public:
    void Initialize();

    void Save();
    void Restore();

private:
#ifdef _wmmx
    // There are 22 WMMX registers should be stored:
    // 16 * 64bits Data Registers
    // 6 * 32bits Control Registers
#define MMX_SIZE                (0x98)

    void SaveWMMX();
    void RestoreWMMX();
#endif // _wmmx

private:
    uint32_t    m_uFaultStatus;
    virtaddr_t  m_vaFaultAddress;

#ifdef _wmmx
    uint32_t m_uMmxFlags; // Judge whether this thread starts to use WMMX
    ulong_t *pMmxData;
    byte_t mmxData[MMX_SIZE + 4];
#endif // _wmmx
};

INLINE void ThreadHContext::Initialize()
{
    m_uFaultStatus = 0;
    m_vaFaultAddress = 0;

#ifdef _wmmx
    m_uMmxFlags = 0;
    // pMmxData should be 8 bytes aligned
    pMmxData = (ulong_t *)(((unsigned long)mmxData + 4) & ~7);
    memset(pMmxData, 0, MMX_SIZE);
#endif
}

#if defined(_GNUC)

#define SWITCH_TO_THREAD0(pThread0) \
    do { \
        ASM("ldr    sp, %0;" \
            "mov    lr, sp;" \
            "b      "C_PREFIX"Thread0Startup;" \
            : :"m"((pThread0)->m_vaKernelStackPtr)); \
    } while(0)
#elif defined(_EVC)

EXTERN_C void SwitchToThread0(virtaddr_t vaStack);

#define SWITCH_TO_THREAD0(pThread0) \
        SwitchToThread0((pThread0)->m_vaKernelStackPtr)

#else

#error Unknown C++ compiler

#endif // defined(_GNUC)

DECL_ASMLINKAGE void ThreadContextSwitch(
        Thread *pPrevThread, Thread *pNextThread);

DECL_ASMLINKAGE void ThreadContextSwitchNoReturn(
        Thread *pNextThread);

#define THREAD_CONTEXT_SWITCH(pPrevThread, pNextThread) \
    do { \
        (pPrevThread)->m_hcontext.Save(); \
        (pNextThread)->m_hcontext.Restore(); \
        ThreadContextSwitch((pPrevThread), (pNextThread)); \
    } while(0)

#define THREAD_CONTEXT_SWITCH_NO_RETURN(pNextThread) \
    do { \
        (pNextThread)->m_hcontext.Restore(); \
        ThreadContextSwitchNoReturn(pNextThread); \
    } while(0)

//
// ProcessHContext
//
enum BreakPointType
{
    BreakPointType_Exec         = 0x01,
    BreakPointType_Write        = 0x02,
    BreakPointType_ReadWrite    = 0x03,
};

class ProcessHContext
{
public:
    void Initialize();

    void Activate();

    ECode SetBreakPoint(
        uint_t uNo, BreakPointType type, void *pvAddress, uint_t uLength);

private:
    // TODO:
};

INLINE void ProcessHContext::Initialize()
{
}

INLINE void ProcessHContext::Activate()
{
}

#ifdef KCONFIG_GLOBAL_BREAKPOINT
EXTERN ECode SetGlobalBreakPoint(
        uint_t uNo, BreakPointType type, void *pvAddress, uint_t uLength);
#endif // KCONFIG_GLOBAL_BREAKPOINT

#endif //__ELASTOS_CONTEXT_H__
